#include "LPC11xx.h"
#include "gpio.h"
#include "timer.h"
#include "adc.h"
#include "utilio.h"
#include "ad9954.h"

/***************************************************************
 * Macro definitions                                           *
 ***************************************************************/
#define us2TimerInterval(us) ( us * (SystemAHBFrequency / 1000000) )
#define ms2TimerInterval(ms) ( ms * (SystemAHBFrequency / 1000) )

/*
 * allocate the timer.
 */
#define TIMER_DELAY         TIMER32[1]
#define TIMER_FREQ_COUNT    TIMER32[0]
#define TIMER_FM_COUNT      TIMER32[0]

#define ADC_SINE_CHANNEL    6


/*
 * define the deviation limit.
 * the deviation of FSK is 100kHz,
 * the deviation of FM is 20kHz.
 * So, I choose the following deviation limits:
 *  NAN < DEVIATION_LIMIT_NAN < DEVIATION_FM < DEVIATION_LIMIT_FSK < DEVIATION_FSK.
 */
// the base wave intermediate frequency.
#define BASE_IF                     ( 455000 )
#define DEVIATION_FM                ( 20000/16 )
#define DEVIATION_FSK               ( 100000/16 )
#define DEVIATION_LIMIT_FSK         ( 2 )
#define DEVIATION_LIMIT_NAN         ( 2 )

#define LF_LIMIT                    ( (1500000>>4) - DEVIATION_FSK )    // frequency will not lower than this.
#define HF_LIMIT                    ( (350000000>>4) + DEVIATION_FSK )  // frequency will not higher than this.

/*
 * define the voltage change threshold.
 */
#define VOLTAGE_DETA_ASK            800     // if deta/Vpp higher han this, is ASK.
#define VOLTAGE_VPP_NAN             100      // if Vpp is lower than this, no modulation.

/***************************************************************
 * Globol variables                                            *
 ***************************************************************/
enum ModulationType {
    Modulation_NAN = 0,
    Modulation_AM,
    Modulation_FM,
    Modulation_ASK,
    Modulation_FSK,
    Modulation_COUNT
};

static const char * ModulationTypeName[Modulation_COUNT] = {
    "NAN",
    "AM",
    "FM",
    "ASK",
    "FSK"
};


extern uint32_t MyStdout;
static volatile uint32_t ddsClock;

static volatile uint32_t carrierFrequency;
static enum ModulationType modulationType;
 
/***************************************************************
 * Globol functions                                            *
 ***************************************************************/
extern void delayus(uint32_t us)
{
    TimerDelayUs(TIMER_DELAY, us);
}

/***************************************************************
 * Functions                                                   *
 ***************************************************************/

/*
 * calculate Local Oscillator.
 * param:   carrier frequency.
 */
static uint32_t calcLO(uint32_t cf)
{
    return ( (cf > BASE_IF) ? cf - BASE_IF : 0 );
}

static uint32_t calcRF(void)
{
    static const uint32_t delayUsTime = 50000;   // 50 modulation cycle.
    static const uint32_t averageTimes = 5;
    volatile double f = 0, maxf = 0, minf = 0xffffffff, freq = 0;
    volatile uint32_t tmr32b0tc1, tmr32b0tc2;
    uint32_t num = averageTimes;                // calculate 10 times average.
    
    TimerEnable(TIMER_FREQ_COUNT);
    
    do {
        // reset and start counting.
        TIMER_FREQ_COUNT->TC = 0;
        tmr32b0tc1 = TIMER_FREQ_COUNT->TC;
        TimerDelayUs(TIMER_DELAY, delayUsTime);
        tmr32b0tc2 = TIMER_FREQ_COUNT->TC;
        
        // the time wasted on software pross is 12.5 us (in 48MHz mode).
        f = (tmr32b0tc2 - tmr32b0tc1) * (1000000 /(double) (delayUsTime + 12.5f));
        maxf = ( (f > maxf) ? f : maxf );
        minf = ( (f < minf) ? f : minf );
        freq += f;
    } while (--num != 0);
    
    // calculate average frequncy without maxf and minf.
    freq = freq - maxf - minf;
    freq /= (averageTimes - 2);
    
    return (freq * 16);
}

static enum ModulationType getAmModulation(void)
{
    static const uint32_t samplesCount = 500;
    uint32_t prevV, currV, maxV = 0, minV = 0xffffffff, Vpp;
    uint32_t tmpDeta, maxDeta = 0;
    uint32_t i;
    
    currV = prevV = ADCRead(ADC_SINE_CHANNEL);  // read first voltage.
    for (i = 0; i < samplesCount; ++i) {
        prevV = currV;
	    currV = ADCRead(ADC_SINE_CHANNEL);  /* Polling */
        
        maxV = ( (currV > maxV) ? currV : maxV );
        minV = ( (currV < minV) ? currV : minV );
        
        tmpDeta = currV - prevV;
        maxDeta = ( ((tmpDeta > maxDeta) && tmpDeta < 0x3ff) ? tmpDeta : maxDeta );
    }
    
    Vpp = maxV - minV;
    
    // judge.
    if (maxDeta == 0) {
        return Modulation_NAN;
    }
    else if ( (1000*maxDeta/Vpp) > VOLTAGE_DETA_ASK ) {
        return Modulation_ASK;
    }
    else if ( Vpp < VOLTAGE_VPP_NAN ) {
        return Modulation_NAN;
    }
    else {
        return Modulation_AM;
    }
}

static enum ModulationType getFmModulation(void)
{
    // half of modulation cycle to allow frequncy change.
    static const uint32_t delayUsTime = 601;
    volatile uint32_t f = 0, maxf = 0, minf = 0xffffffff, deviation;
    volatile uint32_t tmr32b0tc1, tmr32b0tc2;
    uint32_t count4freq = 1000;
    
    TimerEnable(TIMER_FM_COUNT);
    
    do {
        // reset and start counting.
        TIMER_FM_COUNT->TC = 0;
        tmr32b0tc1 = TIMER_FM_COUNT->TC;
        TimerDelayUs(TIMER_DELAY, delayUsTime);
        tmr32b0tc2 = TIMER_FM_COUNT->TC;
        
        f = (tmr32b0tc2 - tmr32b0tc1);
        maxf = ( ((f > maxf) && (f < 10000)) ? f : maxf );
        minf = ( ((f < minf) && (f > 10)) ? f : minf );
    } while (--count4freq != 0);
    
    // judge
    deviation = maxf - minf;
    if ( (maxf == 0) || (minf == 0xffffffff) ) {
        // no wave input.
        return Modulation_NAN;
    }
    if ( deviation > DEVIATION_LIMIT_FSK ) {
        return Modulation_FSK;
    }
    else if ( deviation < DEVIATION_LIMIT_NAN) {
        return Modulation_NAN;
    }
    else {
        return Modulation_FM;
    }
}

static enum ModulationType getModulation(void)
{
    enum ModulationType type;
    if ( (type = getFmModulation()) != Modulation_NAN) {
        return type;
    }
    if ( (type = getAmModulation()) != Modulation_NAN) {
        return type;
    }
    return Modulation_NAN;
}

static void showResult(void)
{
    printf("\f");
    printf("Freq:\t%02.4fMHz\n", (double)carrierFrequency/1000000);
    printf("Type:\t%s\n", ModulationTypeName[modulationType]);
    TimerDelayMs(TIMER_DELAY, 1000);
}

/***************************************************************
 * MAIN                                                        *
 ***************************************************************/
int main (void)
{
    SystemInit();             /* MUST BE INCLUDED! */

    GPIOInit();
    GPIOSetDir(PORT2, 7, GPIO_DIR_OUTPUT);

    // for delay
    TimerInit(TIMER_DELAY);
    
    // for freq capture.
    TimerInitCounter(TIMER_FREQ_COUNT);
    
    // for FM/FSK capture.
    TimerInitCounter(TIMER_FM_COUNT);
    
    ADCInit(ADC_CLK);

    ad9954_init();
    ddsClock = ad9954_set_clk(25000000, 20);
    
    MyStdout = UART;
    StiInit();

    while (1) {
        printf("\f");
        printf("Analyzing, please wait...\r\n");
        
        // calculate the RF frequency.
        carrierFrequency = calcRF();
        
        // calculate a LO and send to mixer.
        ad9954_single_tone(30000000, 0, ddsClock);
        
        modulationType = getModulation();
        
        showResult();
        
        TimerDelayMs(TIMER_DELAY, 1000);
        GPIOSetValue(PORT2, 7, !GPIOGetValue(2, 7));
    }
}

/***************************************************************
 * END OF FILE                                                 *
 ***************************************************************/
